package org.example.lotterysystem.service.impl;

import org.example.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import org.example.lotterysystem.common.exception.ServiceExcetion;
import org.example.lotterysystem.common.utils.JacksonUtil;
import org.example.lotterysystem.common.utils.RedisUtil;
import org.example.lotterysystem.controller.param.CreateActivityParam;
import org.example.lotterysystem.controller.param.CreatePrizeByActivityParam;
import org.example.lotterysystem.controller.param.CreateUserByActivityParam;
import org.example.lotterysystem.controller.param.PageParam;
import org.example.lotterysystem.dao.dataobject.ActivityDO;
import org.example.lotterysystem.dao.dataobject.ActivityPrizeDo;
import org.example.lotterysystem.dao.dataobject.ActivityUserDo;
import org.example.lotterysystem.dao.dataobject.PrizeDO;
import org.example.lotterysystem.dao.mapper.*;
import org.example.lotterysystem.service.ActivityService;
import org.example.lotterysystem.service.dto.ActivityDTO;
import org.example.lotterysystem.service.dto.ActivityDetailDTO;
import org.example.lotterysystem.service.dto.CreateActivityDTO;
import org.example.lotterysystem.service.dto.PageListDTO;
import org.example.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import org.example.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import org.example.lotterysystem.service.enums.ActivityStatusEnum;
import org.example.lotterysystem.service.enums.ActivityUserStatusEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class ActivityServiceImpl implements ActivityService {
    private static final Logger logger = LoggerFactory.getLogger(ActivityServiceImpl.class);

    /**
     * 活动缓存前缀，为了区分业务
     */
    private final String ACTIVITY_PREFIX = "ACTIVITY_";
    /**
     * 活动缓存过期时间
     */
    private final Long ACTIVITY_TIMEOUT = 60 * 60 * 24 * 3L;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PrizeMapper prizeMapper;

    @Autowired
    private ActivityMapper activityMapper;

    @Autowired
    private ActivityUserMapper activityUserMapper;

    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private RedisUtil redisUtil;

    @Override
    @Transactional(rollbackFor = Exception.class)//涉及多表
    public CreateActivityDTO createActivity(CreateActivityParam param) {
        //校验活动信息是否正确
        checkActivityInfo(param);
        //保存活动信息
        ActivityDO activityDO = new ActivityDO();
        activityDO.setActivityName(param.getActivityName());
        activityDO.setDescription(param.getDescription());
        activityDO.setStatus(ActivityStatusEnum.RUNNING.name());
        activityMapper.insert(activityDO);
        //保存活动关联的奖品信息
        List<CreatePrizeByActivityParam>  prizeParams = param.getActivityPrizeList();
        List<ActivityPrizeDo> activityPrizeDoList = prizeParams.stream()
                .map(prizeParam ->{
                    ActivityPrizeDo activityPrizeDo  = new ActivityPrizeDo();
                    activityPrizeDo.setActivityId(activityDO.getId());
                    activityPrizeDo.setPrizeId(prizeParam.getPrizeId());
                    activityPrizeDo.setPrizeAmount(prizeParam.getPrizeAmount());
                    activityPrizeDo.setPrizeTiers(prizeParam.getPrizeTiers());
                    activityPrizeDo.setStatus(ActivityPrizeStatusEnum.INIT.name());
                    return activityPrizeDo;}).collect(Collectors.toList());
        activityPrizeMapper.batchInsert(activityPrizeDoList);
        //保存活动关联的人员信息
        List<CreateUserByActivityParam> userParams = param.getActivityUserList();
        List<ActivityUserDo> activityUserDoList = userParams.stream()
                .map(userParam->{
                    ActivityUserDo activityUserDo = new ActivityUserDo();
                    activityUserDo.setUserId(userParam.getUserId());
                    activityUserDo.setActivityId(activityDO.getId());
                    activityUserDo.setUserName(userParam.getUserName());
                    activityUserDo.setStatus(ActivityUserStatusEnum.INIT.name());
                    return activityUserDo;
                }).collect(Collectors.toList());
        activityUserMapper.batchInsert(activityUserDoList);
        //整合完整的活动信息，存放redis
        //ActivityDetailDTO：活动+奖品+人员

        //先获取奖品基本属性列表
        //获取需要查询的奖品id
        List<Long> prizeIds = param.getActivityPrizeList().stream()
                .map(CreatePrizeByActivityParam::getPrizeId)
                .distinct()
                .collect(Collectors.toList());
        List<PrizeDO> prizeDOList = prizeMapper.batchSelectByIds(prizeIds);
        ActivityDetailDTO activityDetailDTO = convertToActivityDetailDTO(activityDO, activityUserDoList, prizeDOList, activityPrizeDoList);
        cacheActivity(activityDetailDTO);
        //构造返回
        CreateActivityDTO  createActivityDTO = new CreateActivityDTO();
        createActivityDTO.setActivityId(activityDO.getId());
        return createActivityDTO;
    }

    @Override
    public PageListDTO<ActivityDTO> findActivityList(PageParam param) {
        //获取总量
        int total = activityMapper.count();

        //获取当前页列表
        List<ActivityDO> activityDOList = activityMapper.selectActivityList(param.offset(),param.getPageSize());
        List<ActivityDTO> activityDTOList = activityDOList
                .stream()
                .map(activityDO->{
                    ActivityDTO activityDTO = new ActivityDTO();
                    activityDTO.setActivityId(activityDO.getId());
                    activityDTO.setActivityName(activityDO.getActivityName());
                    activityDTO.setDescription(activityDO.getDescription());
                    activityDTO.setStatus(ActivityStatusEnum.forname(activityDO.getStatus()));
                    return  activityDTO;
                }).collect(Collectors.toList());
        return new PageListDTO<>(total,activityDTOList);
    }

    @Override
    public ActivityDetailDTO getActivityDetail(Long activityId) {
        if(null == activityId){
            logger.warn("查询活动详细信息失败，activityId为空！");
            return null;
        }
        //查询redis
        ActivityDetailDTO  detailDTO = getActivityFromCache(activityId);
        if(null != detailDTO){
           logger.info("查询活动详细信息成功!,detailDTO：{}",JacksonUtil.writeValueAsString(detailDTO));
           return detailDTO;
        }
        //如果redis不存在，查表

        //活动表
        ActivityDO aDO = activityMapper.selectById(activityId);
        //活动奖品表
       List<ActivityPrizeDo> apDOList =  activityPrizeMapper.selectByActivityId(activityId);
        //活动人员表
       List<ActivityUserDo> auDOList =  activityUserMapper.selectByActivityId(activityId);
        //奖品表:先获取要查询的奖品id
        List<Long> prizeIds = apDOList
                .stream()
                .map(ActivityPrizeDo::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDO> pDOList = prizeMapper.batchSelectByIds(prizeIds);
        //整合活动详细信息，存放redis
        ActivityDetailDTO activityDetailDTO = convertToActivityDetailDTO(aDO, auDOList, pDOList, apDOList);
        cacheActivity(activityDetailDTO);
        //返回
        return activityDetailDTO;
    }

    @Override
    public void cacheActivity(Long activityId) {
        if(null == activityId){
            logger.warn("缓存的活动id为空！");
            throw  new ServiceExcetion(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_IS_EMPTY);
        }
        //查询表数据：活动表、关联奖品、关联人员、奖品信息表
        //活动表
        ActivityDO aDO = activityMapper.selectById(activityId);
        if(null == aDO){
            logger.error("要缓存的活动id有误！");
            throw  new ServiceExcetion(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_ERROR);

        }
        //活动奖品表
        List<ActivityPrizeDo> apDOList =  activityPrizeMapper.selectByActivityId(activityId);
        //活动人员表
        List<ActivityUserDo> auDOList =  activityUserMapper.selectByActivityId(activityId);
        //奖品表:先获取要查询的奖品id
        List<Long> prizeIds = apDOList
                .stream()
                .map(ActivityPrizeDo::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDO> pDOList = prizeMapper.batchSelectByIds(prizeIds);
        //整合完整的活动信息并缓存
        //整合活动详细信息，存放redis
     cacheActivity(convertToActivityDetailDTO(aDO,auDOList,pDOList,apDOList));

    }

    /**
     * 缓存完整的活动信息  AxtivityDetailDTO
     * @param activityDetailDTO
     */
    private void cacheActivity(ActivityDetailDTO activityDetailDTO) {
        //key:ACTIVITY_activityId
        //value:ActivityDetailDTO（json）
        if(null == activityDetailDTO||null == activityDetailDTO.getActivityId()){
            logger.warn("缓存的活动信息不存在！");
            return;
        }
        //如果存入redis这一步出现异常，我们不希望进行回滚的
        try {
            redisUtil.set(ACTIVITY_PREFIX + activityDetailDTO.getActivityId(),
                    JacksonUtil.writeValueAsString(activityDetailDTO),
                    ACTIVITY_TIMEOUT);
        }catch (Exception  e){
            logger.error("缓存活动异常，ActivityDetailDTO = {}",JacksonUtil.writeValueAsString(activityDetailDTO),e);
        }

    }

    /**
     * 根据活动id从缓存中获取活动详细信息
     * @param activityId
     * @return
     */
    private ActivityDetailDTO getActivityFromCache(Long activityId){
        if(null == activityId){
            logger.warn("从缓存中获取活动数据的activityId为空");
            return null;
        }
        try{
            String redisStr = redisUtil.get(ACTIVITY_PREFIX + activityId);
            if(!StringUtils.hasText(redisStr)){
                logger.info("从缓存中获取活动数据为空：key={}",ACTIVITY_PREFIX + activityId);
                return null;
            }
            ActivityDetailDTO activityDetailDTO = JacksonUtil.readValue(redisStr, ActivityDetailDTO.class);
            return activityDetailDTO;
        } catch (Exception e) {
            logger.error("从缓存中获取活动信息异常：key={}",ACTIVITY_PREFIX + activityId,e);
            return null;
        }
    }

    /**
     * 根据基本DO整合完整的活动信息
     * @param activityDO
     * @param activityUserDoList
     * @param prizeDOList
     * @param activityPrizeDoList
     * @return
     */
    private ActivityDetailDTO convertToActivityDetailDTO(ActivityDO activityDO
            , List<ActivityUserDo> activityUserDoList
            , List<PrizeDO> prizeDOList
            , List<ActivityPrizeDo> activityPrizeDoList) {
        ActivityDetailDTO detailDTO = new ActivityDetailDTO();
        detailDTO.setActivityId(activityDO.getId());
        detailDTO.setActivityName(activityDO.getActivityName());
        detailDTO.setDesc(activityDO.getDescription());
        detailDTO.setStatus(ActivityStatusEnum.forname(activityDO.getStatus()));

        //apDO:{prizeId,amount,status},{prizeId，amount,status}
        //pDO:{prizeid，name……},{prizeid，name……}
        List<ActivityDetailDTO.PrizeDTO> prizeDTOList = activityPrizeDoList
                .stream()
                .map(apDO->{
                    ActivityDetailDTO.PrizeDTO prizeDTO = new ActivityDetailDTO.PrizeDTO();
                    prizeDTO.setPrizeId(apDO.getPrizeId());

                    Optional<PrizeDO> optionalPrizeDO = prizeDOList.stream()
                            .filter(prizeDO -> {
                                return prizeDO.getId().equals(apDO.getPrizeId());
                            }).findFirst();
                    //如果PrizeDO为空，就不会传到参数里，也就不执行当前方法
                    //如果……不为空，则执行当前方法
                    optionalPrizeDO.ifPresent(prizeDO -> {
                        prizeDTO.setName(prizeDO.getName());
                        prizeDTO.setImageUrl(prizeDO.getImageUrl());
                        prizeDTO.setPrice(prizeDO.getPrice());
                        prizeDTO.setDescription(prizeDTO.getDescription());
                    });
                    prizeDTO.setTiers(ActivityPrizeTiersEnum.forname(apDO.getPrizeTiers()));
                    prizeDTO.setPrizeAmount(apDO.getPrizeAmount());
                    prizeDTO.setStatus(ActivityPrizeStatusEnum.forname(apDO.getStatus()));
                    return  prizeDTO;
                }).collect(Collectors.toList());
        detailDTO.setPrizeDTOList(prizeDTOList);

        List<ActivityDetailDTO.UserDTO> userDTOList = activityUserDoList.stream()
                        .map(auDO->{
                            ActivityDetailDTO.UserDTO userDTO = new ActivityDetailDTO.UserDTO();
                            userDTO.setUserId(auDO.getUserId());
                            userDTO.setUserName(auDO.getUserName());
                            userDTO.setStatus(ActivityUserStatusEnum.forname(auDO.getStatus()));
                            return userDTO;
                        }).collect(Collectors.toList());
        detailDTO.setUserDTOList(userDTOList);
        return detailDTO;
    }

    /**
     * 校验活动有效性
     * @param param
     */
    private void checkActivityInfo(CreateActivityParam param) {
        if(null == param){
            throw new ServiceExcetion(ServiceErrorCodeConstants.CREATE_ACTIVITY_INFO_IS_EMPTY);
        }

        //人员id在人员表中是否存在
        //123->1  2
        List<Long> userIds = param.getActivityUserList()
                .stream()
                .map(CreateUserByActivityParam::getUserId)
                .distinct()//去重
                .collect(Collectors.toList());

        List<Long> existUserIds = userMapper.selectExistByIds(userIds);

        if(CollectionUtils.isEmpty(existUserIds)){
            throw  new ServiceExcetion(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
        }

        userIds.forEach(id->{
            if(!existUserIds.contains(id)){
                throw new ServiceExcetion(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
            }
        });

        //奖品id在奖品表中是否存在
        List<Long> prizeIds = param.getActivityPrizeList()
                .stream()
                .map(CreatePrizeByActivityParam::getPrizeId)
                .distinct()
                .collect(Collectors.toList());
        List<Long> existPrizeIds = prizeMapper.selectExistByIds(prizeIds);

        if(CollectionUtils.isEmpty(existPrizeIds)){
            throw  new ServiceExcetion(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
        }

        prizeIds.forEach(id->{
            if(!existPrizeIds.contains(id)){
                throw new ServiceExcetion(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
            }
        });
        //人员数量大于等于奖品数量
       int userAmount = param.getActivityUserList().size();
       long prizeAmount = param.getActivityPrizeList()
                .stream()
                .mapToLong(CreatePrizeByActivityParam::getPrizeAmount)//2 1
                .sum();
       if(userAmount < prizeAmount){
           throw new ServiceExcetion(ServiceErrorCodeConstants.USER_PRIZE_AMOUNT_ERROR);
       }

       //校验和活动奖品等奖的有效性
        param.getActivityPrizeList().forEach(prize->{
            if(null == ActivityPrizeTiersEnum.forname(prize.getPrizeTiers())){
                throw new ServiceExcetion(ServiceErrorCodeConstants.ACTIVITY_PRIZE_TIERS_ERROR);
            }
        });
    }
}
